/*
** menu.c
**
** Pictor, Version 1.51, Copyright (c) 1992-94 SoftCircuits
** Redistributed by permission.
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "pictor.h"


#define STATE_WAIT         0x1000
#define STATE_HILITE       0x1001
#define STATE_RUN          0x1002
#define STATE_CANCEL       0x1003
#define STATE_LEFT         0x2000
#define STATE_RIGHT        0x2001
#define STATE_RUNCOMMAND   0x3000


static int mainselect,subselect;
static int num_menus,num_items,width;

int _PL_menurow = 1;

/*
** Returns the starting column for the given main menu item.
*/
static int getmenucol(MAINMENU *menu,int item)
{
	int i,col = 3;

	for(i = 0;i < item;i++)
		col += (hstrlen(menu[i].item) + 2);

	return(col);

} /* getmenucol */

/*
** Paints a hilite bar on the current submenu selection. Note that
** if the specified colors are the non-hilite colors, then the effect
** is that of removing the hilite bar.
*/
static void hilite_submenu(SUBMENU *menu,int color,int hcolor,int col)
{
	setvpos(subselect + 2 + _PL_menurow,col - 1);
	vrepa(color,width + 2);

	vcolor(color);
	setvpos(subselect + 2 + _PL_menurow,col);
	hputs(menu[subselect].item,hcolor);

} /* hilite_submenu */

/*
** Runs the specified submenu.
*/
static int submenu(SUBMENU *menu,COLORSTRUCT *colors,int col)
{
	int state,state_value;
	int i,key,alt_released = FALSE;

	/* get menu dimensions */
	for(width = 0,num_items = 0;menu[num_items].item != NULL;num_items++) {
		if(menu[num_items].item != (char *)-1) {
			if(hstrlen(menu[num_items].item) > width)
				width = hstrlen(menu[num_items].item);
		}
	}
	if(width == 0)    /* empty menu */
		return STATE_WAIT;

	/* paint submenu */
	wopen(_PL_menurow + 1,col - 2,num_items + 2,width + 4,colors->normal,
		WO_STATICMEM | WO_SHADOW);
	vcolor(colors->normal);
	for(i = 0;menu[i].item != NULL;i++) {
		if(menu[i].item != (char *)-1) {
			setvpos(i + _PL_menurow + 2,col);
			hputs(menu[i].item,colors->boldnormal);
		}
		else {  /* menu divider */
			setvpos(i + _PL_menurow + 2,col - 2);
			vputc('\xC7');
			vrepc('\xC4',width + 2);
			vputc('\xB6');
		}
	}

	state = STATE_HILITE;
	subselect = state_value = 0;

	while(state != STATE_CANCEL) {
		switch(state) {
			case STATE_WAIT:
				if(kbdshift() & 0x08) {
					while((kbdshift() & 0x08) && !kbdready())
						;

					if(!kbdready() && alt_released) {
						state = STATE_CANCEL;
						state_value = STATE_WAIT;
					}
				}
				alt_released = TRUE;

				if(kbdready()) {
					switch(key = kbdread()) {
						case DOWN_KEY:
						case SPACE_BAR:
							state = STATE_HILITE;
							state_value = subselect;
							do {
								if(++state_value >= num_items)
									state_value = 0;
							} while(menu[state_value].item == (char *)-1);
							break;
						case UP_KEY:
							state = STATE_HILITE;
							state_value = subselect;
							do {
								if(--state_value < 0)
									state_value = (num_items - 1);
							} while(menu[state_value].item == (char *)-1);
							break;
						case RIGHT_KEY:
							state = STATE_CANCEL;
							state_value = STATE_RIGHT;
							break;
						case LEFT_KEY:
							state = STATE_CANCEL;
							state_value = STATE_LEFT;
							break;
						case ENTER_KEY:
							state = STATE_CANCEL;
							state_value = STATE_RUNCOMMAND;
							break;
						case ESCAPE_KEY:
							state = STATE_CANCEL;
							state_value = STATE_CANCEL;
							break;
						case F1_KEY:
							if(_PL_helpfunc != NULL)
								_PL_helpfunc(menu[subselect].helptopic);
							else beep();
							break;
						default:
							/* test for hotkey */
							if(isalnum(key = tolower(key & 0xFF))) {
								for(i = 0;i < num_items;i++) {
									if(key == tolower(gethotkey(menu[i].item))) {
										state = STATE_CANCEL;
										state_value = STATE_RUNCOMMAND;
										subselect = i;
										break;
									}
								}
							}
							if(state == STATE_WAIT) beep();
							break;
					}
				}
				break;
			case STATE_HILITE:
				hilite_submenu(menu,colors->normal,colors->boldnormal,col);
				subselect = state_value;
				hilite_submenu(menu,colors->select,colors->boldselect,col);
				if(menu[subselect].description != NULL)
					statusbar(menu[subselect].description);
				state = STATE_WAIT;
				break;
		}
	}
	wclose();

	return(state_value);

} /* submenu */

/*
** Displays the mainmenu across the top of the screen. Hotkeys
** are displayed bold if hilite is TRUE.
*/
void showmenu(MAINMENU *menu,COLORSTRUCT *colors,int hilite)
{
	int i;

	setvpos(_PL_menurow,1);
	vcolor(colors->normal);
	vrepc(' ',_PL_columns);

	for(i = 0;menu[i].item != NULL;i++) {
		setvpos(_PL_menurow,getmenucol(menu,i));
		hputs(menu[i].item,(hilite) ? colors->boldnormal : colors->normal);
	}

} /* showmenu */

/*
** Puts a hilite bar on the current main menu selection. Note that
** if normal attributes are passed, the effect is that of removing
** the hilite bar.
*/
static void hilite_menu(MAINMENU *menu,int color,int hcolor)
{
	int col;

	col = getmenucol(menu,mainselect);

	setvpos(_PL_menurow,col - 1);
	vrepa(color,hstrlen(menu[mainselect].item) + 2);

	if(hcolor != color) {
		vcolor(color);
		setvpos(_PL_menurow,col);
		hputs(menu[mainselect].item,hcolor);
	}

} /* hilite_menu */

/*
** Runs the specified main menu.
*/
static int mainmenu(MAINMENU *menu,COLORSTRUCT *colors,
	COLORSTRUCT *subcolors,int index)
{
	int i,key,state,state_value;

	if(index == -1) {
		state = STATE_HILITE;
		mainselect = state_value = 0;
		showmenu(menu,colors,TRUE);
	}
	else {
		state = STATE_RUN;
		mainselect = state_value = index;
	}

	/* count number of submenus */
	for(num_menus = 0;menu[num_menus].item != NULL;num_menus++)
		;

	pushstatus();
	pushcurs();
	setcurs(_PL_rows + 1,1);

	while(state != STATE_CANCEL) {
		switch(state) {
			case STATE_WAIT:
				if(kbdshift() & 0x08) {
					state = STATE_CANCEL;
					state_value = STATE_WAIT;
				}
				else if(kbdready()) {
					switch(key = kbdread()) {
						case RIGHT_KEY:
						case SPACE_BAR:
							state_value = mainselect;
							if(++state_value >= num_menus)
								state_value = 0;
							state = STATE_HILITE;
							break;
						case LEFT_KEY:
							state_value = mainselect;
							if(--state_value < 0)
								state_value = (num_menus-1);
							state = STATE_HILITE;
							break;
						case ENTER_KEY:
						case DOWN_KEY:
						case UP_KEY:
							state_value = mainselect;
							state = STATE_RUN;
							break;
						case ESCAPE_KEY:
							state = STATE_CANCEL;
							state_value = STATE_CANCEL;
							break;
						case F1_KEY:
							if(_PL_helpfunc != NULL)
								_PL_helpfunc(menu[mainselect].helptopic);
							else beep();
							break;
						default:
							/* test for hotkey */
							if(isalnum(key = tolower(key & 0xFF))) {
								for(i = 0;i < num_menus;i++) {
									if(key == tolower(gethotkey(menu[i].item))) {
										state_value = i;
										state = STATE_RUN;
										break;
									}
								}
							}
							if(state == STATE_WAIT) beep();
							break;
					}
				}
				break;
			case STATE_HILITE:
				hilite_menu(menu,colors->normal,colors->boldnormal);
				mainselect = state_value;
				hilite_menu(menu,colors->select,colors->boldselect);
				if(menu[mainselect].description != NULL)
					statusbar(menu[mainselect].description);
				state = STATE_WAIT;
				break;

			case STATE_RUN:
				while(state == STATE_RUN) {

					/* remove bold hotkeys */
					showmenu(menu,colors,FALSE);

					mainselect = state_value;
					hilite_menu(menu,colors->select,colors->select);

					i = submenu(menu[mainselect].submenu,subcolors,
						getmenucol(menu,mainselect));

					switch(i) {
						case STATE_RIGHT:
							if(++state_value >= num_menus)
								state_value = 0;
							break;
						case STATE_LEFT:
							if(--state_value < 0)
								state_value = (num_menus-1);
							break;
						case STATE_WAIT:
							showmenu(menu,colors,TRUE);
							state = STATE_HILITE;
							state_value = mainselect;
							break;
						default:
							state = STATE_CANCEL;
							state_value = i;

					}
				}
				break;
		}
	}
	popcurs();
	popstatus();
	if(state_value == STATE_RUNCOMMAND) {
		menu[mainselect].submenu[subselect].function();
		state_value = STATE_CANCEL;
	}
	showmenu(menu,colors,FALSE);

	return(state_value);

} /* mainmenu */

/*
** Waits for the next key press. If the key is used by the menu
** routines, it is handled appropriately. Otherwise this function
** returns the value of the key.
**
** If a menu command is selected, the corresponding function is
** called from within the menu routines after which this routine
** will return 0.
*/
int runmenu(MAINMENU *menu,COLORSTRUCT *colors,COLORSTRUCT *colors2)
{
	int i,state = STATE_WAIT,state_value = 0;
	int alt_released = FALSE;

	while(state != STATE_CANCEL) {
		switch(state) {
			case STATE_WAIT:
				if(kbdshift() & 0x08) {
					showmenu(menu,colors,TRUE);
					while((kbdshift() & 0x08) && !kbdready())
						;
					showmenu(menu,colors,FALSE);

					if(alt_released) {
						state = STATE_CANCEL;
						state_value = 0;
					}
					else {
						state = STATE_RUN;   /* assume no key pressed */
						state_value = -1;
					}
				}
				if(kbdready()) {
					state = STATE_CANCEL;
					state_value = kbdread();
					for(i = 0;menu[i].item != NULL;i++) {
						if(getaltkey(gethotkey(menu[i].item)) == state_value) {
							state = STATE_RUN;
							state_value = i;
							break;
						}
					}
				}
				break;
			case STATE_RUN:
				state = mainmenu(menu,colors,colors2,state_value);
				alt_released = TRUE;
				state_value = 0;
				break;
		}
	}
	return(state_value);

} /* runmenu */
